package com.x2.base.jdbc;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.x2.core.util.EmptyUtil;
import com.x2.core.util.ListUtil;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @param <T>
 * @author liuq
 */
@Data
public class JdbcWrapper<T> {

    //内部类
    private Class<T> clasz;

    //表元信息
    private TableMeta meta;

    //条件
    private List<String> condList = new ArrayList<>();

    //参数
    private List<Object> paraList = new ArrayList<>();

    /**
     * 构造函数
     *
     * @param inClasz
     */
    private JdbcWrapper(Class<T> inClasz) {
        clasz = inClasz;
        meta = TableMeta.get(inClasz);
    }

    /**
     * 初始化
     *
     * @param inClasz
     * @return
     */
    public static JdbcWrapper init(Class inClasz) {
        JdbcWrapper w = new JdbcWrapper(inClasz);
        return w;
    }

    /**
     * 添加 and 相等 条件
     *
     * @param column
     * @param value
     * @return
     */
    public JdbcWrapper and(String column, Object value) {
        return and(column, "=", value);
    }


    /**
     * 添加 and 条件
     *
     * @param column
     * @param oper
     * @param value
     * @return
     */
    public JdbcWrapper and(String column, String oper, Object value) {
        if (value == null) {
            return this;
        }
        condList.add(" and " + column + " " + oper + "? ");
        paraList.add(value);
        return this;
    }

    /**
     * 添加 and Like 条件
     *
     * @param column
     * @param value
     * @return
     */
    public JdbcWrapper andLike(String column, Object value) {
        if (value == null) {
            return this;
        }
        condList.add(" and " + column + "  like concat('%',concat(?,'%')) ");
        paraList.add(value);
        return this;
    }

    /**
     * 添加 and 空条件
     *
     * @param column
     * @return
     */
    public JdbcWrapper andNull(String column) {
        condList.add(" and " + column + "is null ");
        return this;
    }

    /**
     * 添加 and 非空条件
     *
     * @param column
     * @return
     */
    public JdbcWrapper andNotNull(String column) {
        condList.add(" and " + column + "is not null ");
        return this;
    }

    /**
     * 添加 and in 条件
     *
     * @param column
     * @param values
     * @return
     */
    public JdbcWrapper andIn(String column, List<Object> values) {
        if (values == null || values.size() <= 0) {
            return this;
        }
        condList.add(" and " + column + " in  (");
        for (int i = 0; i < values.size(); i++) {
            if (i > 0) {
                condList.add(",? ");
            } else {
                condList.add("? ");
            }

            paraList.add(values.get(i));
        }
        condList.add(" )  ");
        return this;
    }

    /**
     * 构建查询语句
     *
     * @return
     */
    public JdbcResult select() {
        List<FieldMeta> fieldVOList = meta.getFieldList();
        StringBuilder sql = new StringBuilder();
        sql.append(" select ");
        FieldMeta fieldVO;
        for (int i = 0; i < fieldVOList.size(); i++) {
            fieldVO = fieldVOList.get(i);
            if (EmptyUtil.empty(fieldVO.getColumnName())) {
                continue;
            }
            if (i > 0) {
                sql.append(",");
            }
            sql.append("t." + fieldVO.getColumnName() + " AS " + fieldVO.getFieldName());
        }
        sql.append(" from ").append(meta.getTableName()).append(" t ");
        sql.append(where());
        sql.append(" ");
        return new JdbcResult(sql.toString(), paraList);
    }

    /**
     * 构建插入语句
     *
     * @param cond
     * @return
     */
    public JdbcResult insert(T cond) {
        if (cond == null) {
            return null;
        }
        Map<String, Object> propList = BeanUtil.beanToMap(cond);
        if (propList == null || propList.size() == 0) {
            return null;
        }
        List<FieldMeta> fieldVOList = meta.getFieldList();
        Map<String, FieldMeta> fieldVOMap = ListUtil.map(fieldVOList, item -> item.getFieldName());

        List<String> condList = new ArrayList<>();
        List<Object> insertParaList = new ArrayList<>();
        StringBuilder insertColumn = new StringBuilder();
        Object pk = null;
        FieldMeta vo;
        for (Map.Entry<String, Object> entry : propList.entrySet()) {
            vo = fieldVOMap.get(entry.getKey());
            if (vo == null) {
                continue;
            }
            if (EmptyUtil.empty(vo.getColumnName())) {
                continue;
            }
            if (entry.getValue() == null) {
                if (IdType.ASSIGN_ID.toString().equalsIgnoreCase(vo.getType())) {
                    if (insertColumn.length() > 0) {
                        insertColumn.append(",");
                    }
                    insertColumn.append(vo.getColumnName());
                    condList.add(" ? ");
                    pk = entry.getValue();
                    if (entry.getValue() == null) {
                        pk = IdUtil.getSnowflake(1, 2).nextId();
                    }
                    insertParaList.add(pk);
                }
            } else {
                if (insertColumn.length() > 0) {
                    insertColumn.append(",");
                }
                insertColumn.append(vo.getColumnName());
                condList.add(" ? ");
                insertParaList.add(entry.getValue());
            }


        }
        StringBuilder sql = new StringBuilder();
        sql.append(" insert into ").append(meta.getTableName()).append(" (");
        sql.append(insertColumn.toString());
        sql.append(") values ( ");
        sql.append(ListUtil.join(condList));
        sql.append(" ) ");
        if (insertParaList.size() <= 0) {
            return null;
        }
        return new JdbcResult(pk, sql.toString(), insertParaList);
    }

    /**
     * 构建主键更新语句
     *
     * @param data
     * @return
     */
    public JdbcResult updateById(T data) {
        return update(data, null, true);
    }

    /**
     * 构建更新语句
     *
     * @param data              数据对象
     * @param mustUpdateKeyList 必须更新字段
     * @return
     */
    public JdbcResult updateById(T data, List<String> mustUpdateKeyList) {
        return update(data, mustUpdateKeyList, true);
    }

    /**
     * 构建更新语句
     *
     * @param data              数据对象
     * @param mustUpdateKeyList 必须更新字段
     * @return
     */
    public JdbcResult update(T data, List<String> mustUpdateKeyList) {
        return update(data, mustUpdateKeyList, false);
    }

    /**
     * 构建更新语句
     *
     * @param data              数据对象
     * @param mustUpdateKeyList 必须更新字段
     * @param needPk            是否需要主键
     * @return
     */
    private JdbcResult update(T data, List<String> mustUpdateKeyList, boolean needPk) {
        if (data == null) {
            return null;
        }
        Map<String, Object> propList = BeanUtil.beanToMap(data);
        if (propList == null || propList.size() == 0) {
            return null;
        }
        List<FieldMeta> fieldVOList = meta.getFieldList();
        Map<String, FieldMeta> fieldVOMap = ListUtil.map(fieldVOList, item -> item.getFieldName());
        List<Object> updateParaList = new ArrayList<>();
        StringBuilder updateColumn = new StringBuilder();
        FieldMeta vo;
        boolean mustUpdateMode = mustUpdateKeyList == null || mustUpdateKeyList.size() <= 0 ? false : true;
        for (Map.Entry<String, Object> entry : propList.entrySet()) {
            vo = fieldVOMap.get(entry.getKey());
            if (vo == null) {
                continue;
            }
            if (EmptyUtil.empty(vo.getColumnName())) {
                continue;
            }
            //主键
            if (vo.getType() != null) {
                //需要作为条件
                if (needPk) {
                    and(vo.getColumnName(), entry.getValue());
                }
                continue;
            }
            if (mustUpdateMode) {
                //不是更新字段不能更新
                if (!mustUpdateKeyList.contains(vo.getColumnName()) && !mustUpdateKeyList.contains(vo.getFieldName())) {
                    continue;
                }
                if (updateColumn.length() > 0) {
                    updateColumn.append(",");
                }
                if (entry.getValue() == null) {
                    updateColumn.append(vo.getColumnName()).append("= null");
                } else {
                    updateColumn.append(vo.getColumnName()).append("=").append("?");
                    updateParaList.add(entry.getValue());
                }
            } else {
                if (entry.getValue() == null) {
                    continue;
                }
                if (updateColumn.length() > 0) {
                    updateColumn.append(",");
                }
                updateColumn.append(vo.getColumnName()).append("=").append("?");
                updateParaList.add(entry.getValue());
            }
        }
        StringBuilder sql = new StringBuilder();
        sql.append(" update ").append(meta.getTableName()).append(" set ");
        sql.append(updateColumn.toString());
        sql.append(where());
        sql.append(" ");
        updateParaList.addAll(paraList);
        return new JdbcResult(sql.toString(), updateParaList);
    }

    /**
     * 构建主键删除语句
     *
     * @param data
     * @return
     */
    public JdbcResult removeById(T data) {
        return remove(data, true);
    }

    /**
     * 构建删除语句
     *
     * @param data
     * @return
     */
    public JdbcResult remove(T data) {
        return remove(data, false);
    }

    /**
     * 构建删除语句
     *
     * @param data
     * @return
     */
    private JdbcResult remove(T data, boolean needPk) {
        if (data == null) {
            return null;
        }
        Map<String, Object> propList = BeanUtil.beanToMap(data);
        if (propList == null || propList.size() == 0) {
            return null;
        }
        List<FieldMeta> fieldVOList = meta.getFieldList();
        Map<String, FieldMeta> fieldVOMap = ListUtil.map(fieldVOList, item -> item.getFieldName());
        FieldMeta vo;
        for (Map.Entry<String, Object> entry : propList.entrySet()) {
            vo = fieldVOMap.get(entry.getKey());
            if (vo == null) {
                continue;
            }
            if (entry.getValue() == null) {
                continue;
            }
            if (vo.getType() != null) {
                if (needPk) {
                    and(vo.getColumnName(), entry.getValue());
                }
            }
        }
        StringBuilder sql = new StringBuilder();
        sql.append(" delete from ").append(meta.getTableName()).append(" ");
        sql.append(where());
        sql.append(" ");
        return new JdbcResult(sql.toString(), paraList);
    }

    /**
     * where 條件
     *
     * @return
     */
    private String where() {
        StringBuilder w = new StringBuilder();
        if (condList.size() > 0) {
            w.append(" where 1=1");
            for (String cond : condList) {
                w.append(cond);
            }
        }
        return w.toString();
    }
}
